﻿//LoggingHelper.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

using System.Diagnostics;

namespace StockSharp.Logging
{
	using System;
	using System.Linq;
	using System.Reflection;

	using Ecng.Common;
	using Ecng.Configuration;
	using Ecng.Net;

	/// <summary>
	/// Вспомогательный класс для работы с <see cref="ILogSource"/>. 
	/// </summary>
	public static class LoggingHelper
	{
		private static readonly Stopwatch _timer;
		private static readonly DateTime _startDate;
		static LoggingHelper()
		{
			_timer = new Stopwatch();
			_startDate = DateTime.Now;
			_timer.Start();
		}

		/// <summary>
		/// Текущее время.
		/// </summary>
		public static DateTime Now
		{
			get { return _startDate + _timer.Elapsed + NowOffset; }
		}

		/// <summary>
		/// Временное смещение. Неоходимо устанавливать, когда торговая программа работает с неточными настройками локального времени.
		/// Значение <see cref="Now"/> будет корректироваться в зависимости от установленного значения.
		/// </summary>
		public static TimeSpan NowOffset { get; set; }

		private static TimeSpan _timeZoneOffset = TimeZoneInfo.Local.BaseUtcOffset;

		/// <summary>
		/// Временное смещение временной зоны.
		/// </summary>
		public static TimeSpan TimeZoneOffset
		{
			get { return _timeZoneOffset; }
			set { _timeZoneOffset = value; }
		}

		/// <summary>
		/// Синхронизировать <see cref="NowOffset"/> между локальным временем на компьютере и NTP сервером в интернете.
		/// </summary>
		/// <param name="timeout">Таймаут синхронизации в милисекундах.</param>
		public static void SyncMarketTime(int timeout = 5000)
		{
			var dtNow = _startDate + _timer.Elapsed;
			NowOffset = new NtpClient().GetLocalTime(TimeZoneInfo.Local, timeout).Subtract(dtNow);
		}


		/// <summary>
		/// Записать сообщение в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="getMessage">Функция, возвращающая текст для <see cref="LogMessage.Message"/>.</param>
		public static void AddInfoLog(this ILogReceiver receiver, Func<string> getMessage)
		{
			receiver.AddLog(LogLevels.Info, getMessage);
		}

		/// <summary>
		/// Записать предупреждение в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="getMessage">Функция, возвращающая текст для <see cref="LogMessage.Message"/>.</param>
		public static void AddWarningLog(this ILogReceiver receiver, Func<string> getMessage)
		{
			receiver.AddLog(LogLevels.Warning, getMessage);
		}

		/// <summary>
		/// Записать ошибку в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="getMessage">Функция, возвращающая текст для <see cref="LogMessage.Message"/>.</param>
		public static void AddErrorLog(this ILogReceiver receiver, Func<string> getMessage)
		{
			receiver.AddLog(LogLevels.Error, getMessage);
		}

		/// <summary>
		/// Записать сообщение в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="level">Уровень лог-сообщения.</param>
		/// <param name="getMessage">Функция, возвращающая текст для <see cref="LogMessage.Message"/>.</param>
		public static void AddLog(this ILogReceiver receiver, LogLevels level, Func<string> getMessage)
		{
			if (receiver == null)
				throw new ArgumentNullException("receiver");

			receiver.AddLog(new LogMessage(receiver, receiver.CurrentTime, level, getMessage));
		}

		/// <summary>
		/// Записать сообщение в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="message">Текстовое сообщение.</param>
		/// <param name="args">Параметры текстового сообщения.
		/// Используются в случае, если message является форматирующей строкой.
		/// Подробнее, <see cref="string.Format(string,object[])"/>.</param>
		public static void AddInfoLog(this ILogReceiver receiver, string message, params object[] args)
		{
			receiver.AddMessage(LogLevels.Info, message, args);
		}

		/// <summary>
		/// Записать отладку в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="message">Текстовое сообщение.</param>
		/// <param name="args">Параметры текстового сообщения.
		/// Используются в случае, если message является форматирующей строкой.
		/// Подробнее, <see cref="string.Format(string,object[])"/>.</param>
		public static void AddDebugLog(this ILogReceiver receiver, string message, params object[] args)
		{
			receiver.AddMessage(LogLevels.Debug, message, args);
		}

		/// <summary>
		/// Записать предупреждение в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="message">Текстовое сообщение.</param>
		/// <param name="args">Параметры текстового сообщения.
		/// Используются в случае, если message является форматирующей строкой.
		/// Подробнее, <see cref="string.Format(string,object[])"/>.</param>
		public static void AddWarningLog(this ILogReceiver receiver, string message, params object[] args)
		{
			receiver.AddMessage(LogLevels.Warning, message, args);
		}

		/// <summary>
		/// Записать ошибку в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="exception">Описание ошибки.</param>
		public static void AddErrorLog(this ILogReceiver receiver, Exception exception)
		{
			if (receiver == null)
				throw new ArgumentNullException("receiver");

			if (exception == null)
				throw new ArgumentNullException("exception");

			receiver.AddLog(new LogMessage(receiver, receiver.CurrentTime, LogLevels.Error, () =>
			{
				var msg = exception.ToString();
				
				var refExc = exception as ReflectionTypeLoadException;

				if (refExc == null)
					return msg;
				else
				{
					return msg
						+ Environment.NewLine
						+ refExc
							.LoaderExceptions
							.Select(e => e.ToString())
							.Join(Environment.NewLine);
				}
			}));
		}

		/// <summary>
		/// Записать ошибку в лог.
		/// </summary>
		/// <param name="receiver">Получатель логов.</param>
		/// <param name="message">Текстовое сообщение.</param>
		/// <param name="args">Параметры текстового сообщения.
		/// Используются в случае, если message является форматирующей строкой.
		/// Подробнее, <see cref="string.Format(string,object[])"/>.</param>
		public static void AddErrorLog(this ILogReceiver receiver, string message, params object[] args)
		{
			receiver.AddMessage(LogLevels.Error, message, args);
		}

		private static void AddMessage(this ILogReceiver receiver, LogLevels type, string message, params object[] args)
		{
			if (receiver == null)
				throw new ArgumentNullException("receiver");

			receiver.AddLog(new LogMessage(receiver, receiver.CurrentTime, type, message, args));
		}

		/// <summary>
		/// Записать ошибку в <see cref="LogManager.Application"/>.
		/// </summary>
		/// <param name="error">Ошибка.</param>
		public static void LogError(this Exception error)
		{
			if (error == null)
				throw new ArgumentNullException("error");

			var manager = ConfigManager.TryGetService<LogManager>();

			if (manager != null)
				manager.Application.AddErrorLog(error);
		}
	}
}
